package com.yumo.kangchenjunga.user;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.servlet.http.HttpSession;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;

import com.google.common.collect.Streams;
import com.yumo.kangchenjunga.company.CompanyEntity;
import com.yumo.kangchenjunga.company.CompanyService;
import com.yumo.kangchenjunga.company.NoSuchCompanyException;
import com.yumo.kangchenjunga.company.NoSuchStoreException;
import com.yumo.kangchenjunga.company.StoreEntity;
import com.yumo.kangchenjunga.company.StoreRepository;

@Service
public final class UserService {

	@Autowired
	private UserRepository repository;
	@Autowired
	private StoreRepository storeRepository;
	@Autowired
	private CompanyService companyService;

	public UserVo create(UserVo user) throws NoSuchCompanyException, NoSuchStoreException {
		CompanyEntity company = companyService.findById(user.companyId);
		StoreEntity store = null;

		if (("store_admin".equals(user.privilege) || "store_powor_user".equals(user.privilege)
				|| "store_user".equals(user.privilege)) && user.storeId == 0) {
			throw new IllegalArgumentException("门店用户必须选择门店");
		}

		if (user.storeId != 0) {
			store = storeRepository.findById(user.storeId).orElseThrow(NoSuchStoreException::new);
		}

		return new UserVo(User.create(repository, new User(user, company, store)));
	}

	public UserVo createCompanyUser(UserVo user) throws NoSuchCompanyException {
		CompanyEntity company = companyService.findById(user.companyId);

		return new UserVo(User.create(repository, new User(user, company, null)));
	}

	public UserVo createStoreUser(UserVo user) throws NoSuchCompanyException, NoSuchStoreException {
		CompanyEntity company = companyService.findById(user.companyId);
		StoreEntity store = null;

		if (user.storeId != 0) {
			store = storeRepository.findById(user.storeId).orElseThrow(NoSuchStoreException::new);
		} else {
			throw new NoSuchStoreException();
		}

		return new UserVo(User.create(repository, new User(user, company, store)));
	}

	public UserVo queryById(int id) throws NoSuchUserException {
		return repository.findById(id).map(UserVo::new).orElseThrow(() -> createNoSuchUserException(id));
	}

	private NoSuchUserException createNoSuchUserException(int userId) {
		return new NoSuchUserException(String.format("userId: %d.", userId));
	}

	public int delete(int id) throws NoSuchUserException {
		UserVo user = queryById(id);

		if (("admin".equals(user.username) && user.companyId == 1)
				|| (CompanyService.ADMIN_NAME.equals(user.username)
						&& CompanyService.ADMIN_PRIVILEGE.equals(user.privilege))) {
			return 0;
		}

		return repository.deleteById(id);
	}

	public UserVo modify(UserVo user) throws NoSuchUserException, NoSuchStoreException {
		Optional<User> row = repository.findById(user.id);

		if (row.isPresent()) {
			User u = row.get();

			u.username = user.username;
			u.privilege = user.privilege;

			if (user.storeId != 0) {
				u.store = storeRepository.findById(user.storeId).orElseThrow(NoSuchStoreException::new);
			}

			if (StringUtils.isNotEmpty(user.password)) {
				u.changePassword(user.password);
			}

			return new UserVo(repository.save(u));
		}

		throw createNoSuchUserException(user.id);
	}

	public List<UserVo> list() {
		return Streams.stream(repository.findAll()).map(UserVo::new).collect(Collectors.toList());
	}

	public Page<UserVo> page(int page, int size) {
		return repository.findAll(PageRequest.of(page, size, Sort.by(Direction.DESC, "id"))).map(UserVo::new);
	}

	public Page<UserVo> pageByPrivilege(int page, int size, String privilege) {
		return repository.findByPrivilege(privilege, PageRequest.of(page, size, Sort.by(Direction.DESC, "id")))
				.map(UserVo::new);
	}

	public Page<UserVo> pageByCompanyId(int page, int size, int companyId) {
		return repository.findByCompanyId(companyId, PageRequest.of(page, size, Sort.by(Direction.DESC, "id")))
				.map(UserVo::new);
	}

	public String login(UserVo user, HttpSession session) {
		Optional<User> row = repository.findByUsernameAndCompanyId(user.username, user.companyId);

		if (row.isPresent()) {
			if (StringUtils.equals(user.username, row.get().username)
					&& StringUtils.equals(DigestUtils.sha256Hex(row.get().salt + user.password), row.get().password)) {
				session.setAttribute("user", row.get());
				return "success";
			}
			return "fail";
		}
		return "fail";
	}

	public User query(String username, int companyId) throws NoSuchUserException {
		return repository.findByUsernameAndCompanyId(username, companyId).orElseThrow(
				() -> new NoSuchUserException(String.format("username: '%s', companyId: %d.", username, companyId)));
	}

	public boolean changePassword(int userId, ChangePasswordVo vo) throws NoSuchUserException {
		Optional<User> row = repository.findById(userId);

		if (row.isPresent()) {
			User user = row.get();

			if (user.verifyPassword(vo.oldPassword)) {
				user.changePassword(vo.newPassword);
				repository.save(user);

				return true;
			}

			return false;
		}

		throw createNoSuchUserException(userId);
	}

	public User queryByIdAndStoreId(int opUserId, int opStoreId) throws NoSuchUserException {
		var user = repository.findByIdAndStoreId(opUserId, opStoreId);

		return user.orElseThrow(() -> new NoSuchUserException(
				String.format("userId: %d, storeId: %d.", opUserId, opStoreId)));
	}

	public void changeCompanyAdminPassword(int companyId, String newPassword) throws NoSuchUserException {
		var row = repository.findByCompanyIdAndUsername(companyId, CompanyService.ADMIN_NAME);

		if (row.isPresent()) {
			User user = row.get();

			user.changePassword(newPassword);
			repository.save(user);

			return;
		}

		throw createNoSuchUserException(-1);
	}

}
